package com.interviews.atomic;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * Atomic\FieldUpdater 原子更新器
 * <p>
 * AtomicIntegerFieldUpdater：原子更新整形的更新器；
 * <p>
 * AtomicLongFieldUpdater：原子更新长整形的更新器；
 * <p>
 * AtomicReferenceFieldUpdater：原子更新引用的更新器。
 *
 * 这段代码就演示了这个类的用法，比如说我们有两个类，它们都是 Score 类型的，Score 类型内部会有一个分数，
 * 也叫作 core，那么这两个分数的实例分别叫作数学 math 和计算机  computer，然后我们还声明了
 * 一个 AtomicIntegerFieldUpdater，在它构造的时候传入了两个参数，第一个是 Score.class，这是我们
 * 的类名，第二个是属性名，叫作 score。
 *
 * 接下来我们看一下 run 方法，run 方法里面会对这两个实例分别进行自加操作。
 *
 * 第一个是 computer，这里的 computer 我们调用的是它内部的 score，也就是说我们直接调用了 int 变
 * 量的自加操作，这在多线程下是线程非安全的。
 *
 * 第二个自加是利用了刚才声明的 scoreUpdater 并且使用了它的 getAndIncrement 方法并且传入了 math，
 * 这是一种正确使用AtomicIntegerFieldUpdater 的用法，这样可以线程安全地进行自加操作。
 *
 * 接下来我们看下 main 函数。在 main 函数中，我们首先把 math 和 computer 定义了出来，然后分别
 * 启动了两个线程，每个线程都去执行我们刚才所介绍过的 run 方法。这样一来，两个 score，也就是 math
 * 和 computer 都会分别被加 2000 次，最后我们在 join 等待之后把结果打印了出来，
 * 这个程序的运行结果如下：
 *
 * 普通变量的结果：1942
 * 升级后的结果：2000
 *
 * 可以看出，正如我们所预料的那样，普通变量由于不具备线程安全性，所以在多线程操作的情况下，它虽然看
 * 似进行了 2000 次操作，但有一些操作被冲突抵消了，所以最终结果小于 2000。可是使用
 * AtomicIntegerFieldUpdater 这个工具之后，就可以做到把一个普通类型的 score 变量进行原子的
 * 自加操作，最后的结果也和加的次数是一样的，也就是 2000。可以看出，这个类的功能还是非常强大的。
 * @author qian
 * @version 1.0
 * @date 2022/3/2 14:00
 */
public class AtomicIntegerFieldUpdaterDemo implements Runnable {

    static Score math;
    static Score computer;

    public static AtomicIntegerFieldUpdater<Score> scoreUpdater = AtomicIntegerFieldUpdater.newUpdater(Score.class, "score");

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            computer.score++;
            scoreUpdater.getAndIncrement(math);
        }
    }

    public static class Score {
        volatile int score;
    }


    public static void main(String[] args) throws InterruptedException {
        math = new Score();
        computer = new Score();

        AtomicIntegerFieldUpdaterDemo updaterDemo = new AtomicIntegerFieldUpdaterDemo();

        Thread t1 = new Thread(updaterDemo);
        Thread t2 = new Thread(updaterDemo);

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("普通变量的结果:"+computer.score);
        System.out.println("升级变量的结果:"+math.score);
    }
}
